home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / tusrc.zip / SRC / NL.C < prev    next >
C/C++ Source or Header  |  1993-09-19  |  14KB  |  569 lines

  1. /* nl -- number lines of files
  2.    Copyright (C) 1989, 1992 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Scott Bartram (nancy!scott@uunet.uu.net)
  19.    Revised by David MacKenzie (djm@gnu.ai.mit.edu) */
  20.  
  21. #include <stdio.h>
  22. #include <sys/types.h>
  23. #include "../lib/getopt.h"
  24. #include "../lib/regex.h"
  25. #include "../lib/linebuffer.h"
  26. #include "system.h"
  27. #include "version.h"
  28.  
  29. #ifndef TRUE
  30. #define TRUE   1
  31. #define FALSE  0
  32. #endif
  33.  
  34. /* Line-number formats. */
  35. enum number_format
  36. {
  37.   FORMAT_RIGHT_NOLZ,        /* Right justified, no leading zeroes.  */
  38.   FORMAT_RIGHT_LZ,        /* Right justified, leading zeroes.  */
  39.   FORMAT_LEFT            /* Left justified, no leading zeroes.  */
  40. };
  41.  
  42. /* Default section delimiter characters.  */
  43. #define DEFAULT_SECTION_DELIMITERS  "\\:"
  44.  
  45. /* Types of input lines: either one of the section delimiters,
  46.    or text to output. */
  47. enum section
  48. {
  49.   Header, Body, Footer, Text
  50. };
  51.  
  52. char *xmalloc ();
  53. char *xrealloc ();
  54. void error ();
  55.  
  56. static enum section check_section ();
  57. static int build_type_arg ();
  58. static int nl_file ();
  59. static void usage ();
  60. static void process_file ();
  61. static void proc_header ();
  62. static void proc_body ();
  63. static void proc_footer ();
  64. static void proc_text ();
  65. static void print_lineno ();
  66. static void build_print_fmt ();
  67.  
  68. /* The name this program was run with. */
  69. char *program_name;
  70.  
  71. /* Format of body lines (-b).  */
  72. static char *body_type = "t";
  73.  
  74. /* Format of header lines (-h).  */
  75. static char *header_type = "n";
  76.  
  77. /* Format of footer lines (-f).  */
  78. static char *footer_type = "n";
  79.  
  80. /* Format currently being used (body, header, or footer).  */
  81. static char *current_type;
  82.  
  83. /* Regex for body lines to number (-bp).  */
  84. static struct re_pattern_buffer body_regex;
  85.  
  86. /* Regex for header lines to number (-hp).  */
  87. static struct re_pattern_buffer header_regex;
  88.  
  89. /* Regex for footer lines to number (-fp).  */
  90. static struct re_pattern_buffer footer_regex;
  91.  
  92. /* Pointer to current regex, if any.  */
  93. static struct re_pattern_buffer *current_regex = NULL;
  94.  
  95. /* Separator string to print after line number (-s).  */
  96. static char *separator_str = "\t";
  97.  
  98. /* Input section delimiter string (-d).  */
  99. static char *section_del = DEFAULT_SECTION_DELIMITERS;
  100.  
  101. /* Header delimiter string.  */
  102. static char *header_del = NULL;
  103.  
  104. /* Header section delimiter length.  */
  105. static int header_del_len;
  106.  
  107. /* Body delimiter string.  */
  108. static char *body_del = NULL;
  109.  
  110. /* Body section delimiter length.  */
  111. static int body_del_len;
  112.  
  113. /* Footer delimiter string.  */
  114. static char *footer_del = NULL;
  115.  
  116. /* Footer section delimiter length.  */
  117. static int footer_del_len;
  118.  
  119. /* Input buffer.  */
  120. static struct linebuffer line_buf;
  121.  
  122. /* printf format string for line number.  */
  123. static char *print_fmt;
  124.  
  125. /* printf format string for unnumbered lines.  */
  126. static char *print_no_line_fmt = NULL;
  127.  
  128. /* Starting line number on each page (-v).  */
  129. static int page_start = 1;
  130.  
  131. /* Line number increment (-i).  */
  132. static int page_incr = 1;
  133.  
  134. /* If TRUE, reset line number at start of each page (-p).  */
  135. static int reset_numbers = TRUE;
  136.  
  137. /* Number of blank lines to consider to be one line for numbering (-l).  */
  138. static int blank_join = 1;
  139.  
  140. /* Width of line numbers (-w).  */
  141. static int lineno_width = 6;
  142.  
  143. /* Line number format (-n).  */
  144. static enum number_format lineno_format = FORMAT_RIGHT_NOLZ;
  145.  
  146. /* Current print line number.  */
  147. static int line_no;
  148.  
  149. /* Nonzero if we have ever read standard input. */
  150. static int have_read_stdin;
  151.  
  152. /* If non-zero, display usage information and exit.  */
  153. static int flag_help;
  154.  
  155. /* If non-zero, print the version on standard error.  */
  156. static int flag_version;
  157.  
  158. static struct option const longopts[] =
  159. {
  160.   {"header-numbering", required_argument, NULL, 'h'},
  161.   {"body-numbering", required_argument, NULL, 'b'},
  162.   {"footer-numbering", required_argument, NULL, 'f'},
  163.   {"first-page", required_argument, NULL, 'v'},
  164.   {"page-increment", required_argument, NULL, 'i'},
  165.   {"no-renumber", no_argument, NULL, 'p'},
  166.   {"join-blank-lines", required_argument, NULL, 'l'},
  167.   {"number-separator", required_argument, NULL, 's'},
  168.   {"number-width", required_argument, NULL, 'w'},
  169.   {"number-format", required_argument, NULL, 'n'},
  170.   {"section-delimiter", required_argument, NULL, 'd'},
  171.   {"help", no_argument, &flag_help, 1},
  172.   {"version", no_argument, &flag_version, 1},
  173.   {NULL, 0, NULL, 0}
  174. };
  175.  
  176. void
  177. main (argc, argv)
  178.      int argc;
  179.      char **argv;
  180. {
  181.   int c, exit_status = 0;
  182.  
  183.   program_name = argv[0];
  184.   have_read_stdin = 0;
  185.  
  186.   while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
  187.                (int *) 0)) != EOF)
  188.     {
  189.       switch (c)
  190.     {
  191.     case 0:
  192.       break;
  193.  
  194.     case 'h':
  195.       if (build_type_arg (&header_type, &header_regex) != TRUE)
  196.         usage ();
  197.       break;
  198.     case 'b':
  199.       if (build_type_arg (&body_type, &body_regex) != TRUE)
  200.         usage ();
  201.       break;
  202.     case 'f':
  203.       if (build_type_arg (&footer_type, &footer_regex) != TRUE)
  204.         usage ();
  205.       break;
  206.     case 'v':
  207.       page_start = atoi (optarg);
  208.       break;
  209.     case 'i':
  210.       page_incr = atoi (optarg);
  211.       if (page_incr < 1)
  212.         page_incr = 1;
  213.       break;
  214.     case 'p':
  215.       reset_numbers = FALSE;
  216.       break;
  217.     case 'l':
  218.       blank_join = atoi (optarg);
  219.       break;
  220.     case 's':
  221.       separator_str = optarg;
  222.       break;
  223.     case 'w':
  224.       lineno_width = atoi (optarg);
  225.       if (lineno_width < 1)
  226.         lineno_width = 1;
  227.       break;
  228.     case 'n':
  229.       switch (*optarg)
  230.         {
  231.         case 'l':
  232.           if (optarg[1] == 'n')
  233.         lineno_format = FORMAT_LEFT;
  234.           else
  235.         usage ();
  236.           break;
  237.         case 'r':
  238.           switch (optarg[1])
  239.         {
  240.         case 'n':
  241.           lineno_format = FORMAT_RIGHT_NOLZ;
  242.           break;
  243.         case 'z':
  244.           lineno_format = FORMAT_RIGHT_LZ;
  245.           break;
  246.         default:
  247.           usage ();
  248.           break;
  249.         }
  250.           break;
  251.         default:
  252.           usage ();
  253.           break;
  254.         }
  255.       break;
  256.     case 'd':
  257.       section_del = optarg;
  258.       break;
  259.     default:
  260.       usage ();
  261.       break;
  262.     }
  263.     }
  264.  
  265.   if (flag_version)
  266.     {
  267.       fprintf (stderr, "%s\n", version_string);
  268.       exit (0);
  269.     }
  270.  
  271.   if (flag_help)
  272.     usage ();
  273.  
  274.   /* Initialize the section delimiters.  */
  275.   c = strlen (section_del);
  276.  
  277.   header_del_len = c * 3;
  278.   header_del = xmalloc (header_del_len + 1);
  279.   strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
  280.  
  281.   body_del_len = c * 2;
  282.   body_del = xmalloc (body_del_len + 1);
  283.   strcat (strcpy (body_del, section_del), section_del);
  284.  
  285.   footer_del_len = c;
  286.   footer_del = xmalloc (footer_del_len + 1);
  287.   strcpy (footer_del, section_del);
  288.  
  289.   /* Initialize the input buffer.  */
  290.   initbuffer (&line_buf);
  291.  
  292.   /* Initialize the printf format for unnumbered lines. */
  293.   c = strlen (separator_str);
  294.   print_no_line_fmt = xmalloc (lineno_width + c + 1);
  295.   memset (print_no_line_fmt, ' ', lineno_width + c);
  296.   print_no_line_fmt[lineno_width + c] = '\0';
  297.  
  298.   line_no = page_start;
  299.   current_type = body_type;
  300.   current_regex = &body_regex;
  301.   build_print_fmt ();
  302.  
  303.   /* Main processing. */
  304.  
  305.   if (optind == argc)
  306.     exit_status |= nl_file ("-");
  307.   else
  308.     for (; optind < argc; optind++)
  309.       exit_status |= nl_file (argv[optind]);
  310.  
  311.   if (have_read_stdin && fclose (stdin) == EOF)
  312.     {
  313.       error (0, errno, "-");
  314.       exit_status = 1;
  315.     }
  316.   if (ferror (stdout) || fclose (stdout) == EOF)
  317.     error (1, errno, "write error");
  318.  
  319.   exit (exit_status);
  320. }
  321.  
  322. /* Process file FILE to standard output.
  323.    Return 0 if successful, 1 if not. */
  324.  
  325. static int
  326. nl_file (file)
  327.      char *file;
  328. {
  329.   FILE *stream;
  330.  
  331.   if (!strcmp (file, "-"))
  332.     {
  333.       have_read_stdin = 1;
  334.       stream = stdin;
  335.     }
  336.   else
  337.     {
  338.       stream = fopen (file, "r");
  339.       if (stream == NULL)
  340.     {
  341.       error (0, errno, "%s", file);
  342.       return 1;
  343.     }
  344.     }
  345.  
  346.   process_file (stream);
  347.  
  348.   if (ferror (stream))
  349.     {
  350.       error (0, errno, "%s", file);
  351.       return 1;
  352.     }
  353.   if (!strcmp (file, "-"))
  354.     clearerr (stream);        /* Also clear EOF. */
  355.   else if (fclose (stream) == EOF)
  356.     {
  357.       error (0, errno, "%s", file);
  358.       return 1;
  359.     }
  360.   return 0;
  361. }
  362.  
  363. /* Read and process the file pointed to by FP. */
  364.  
  365. static void
  366. process_file (fp)
  367.      FILE *fp;
  368. {
  369.   while (readline (&line_buf, fp))
  370.     {
  371.       switch ((int) check_section ())
  372.     {
  373.     case Header:
  374.       proc_header ();
  375.       break;
  376.     case Body:
  377.       proc_body ();
  378.       break;
  379.     case Footer:
  380.       proc_footer ();
  381.       break;
  382.     case Text:
  383.       proc_text ();
  384.       break;
  385.     }
  386.     }
  387. }
  388.  
  389. /* Return the type of line in `line_buf'. */
  390.  
  391. static enum section
  392. check_section ()
  393. {
  394.   if (line_buf.length < 2 || memcmp (line_buf.buffer, section_del, 2))
  395.     return Text;
  396.   if (line_buf.length == header_del_len
  397.       && !memcmp (line_buf.buffer, header_del, header_del_len))
  398.     return Header;
  399.   if (line_buf.length == body_del_len
  400.       && !memcmp (line_buf.buffer, body_del, body_del_len))
  401.     return Body;
  402.   if (line_buf.length == footer_del_len
  403.       && !memcmp (line_buf.buffer, footer_del, footer_del_len))
  404.     return Footer;
  405.   return Text;
  406. }
  407.  
  408. /* Switch to a header section. */
  409.  
  410. static void
  411. proc_header ()
  412. {
  413.   current_type = header_type;
  414.   current_regex = &header_regex;
  415.   if (reset_numbers)
  416.     line_no = page_start;
  417.   putchar ('\n');
  418. }
  419.  
  420. /* Switch to a body section. */
  421.  
  422. static void
  423. proc_body ()
  424. {
  425.   current_type = body_type;
  426.   current_regex = &body_regex;
  427.   putchar ('\n');
  428. }
  429.  
  430. /* Switch to a footer section. */
  431.  
  432. static void
  433. proc_footer ()
  434. {
  435.   current_type = footer_type;
  436.   current_regex = &footer_regex;
  437.   putchar ('\n');
  438. }
  439.  
  440. /* Process a regular text line in `line_buf'. */
  441.  
  442. static void
  443. proc_text ()
  444. {
  445.   static int blank_lines = 0;    /* Consecutive blank lines so far. */
  446.  
  447.   switch (*current_type)
  448.     {
  449.     case 'a':
  450.       if (blank_join > 1)
  451.     {
  452.       if (line_buf.length || ++blank_lines == blank_join)
  453.         {
  454.           print_lineno ();
  455.           blank_lines = 0;
  456.         }
  457.       else
  458.         printf (print_no_line_fmt);
  459.     }
  460.       else
  461.     print_lineno ();
  462.       break;
  463.     case 't':
  464.       if (line_buf.length)
  465.     print_lineno ();
  466.       else
  467.     printf (print_no_line_fmt);
  468.       break;
  469.     case 'n':
  470.       printf (print_no_line_fmt);
  471.       break;
  472.     case 'p':
  473.       if (re_search (current_regex, line_buf.buffer, line_buf.length,
  474.              0, line_buf.length, (struct re_registers *) 0) < 0)
  475.     printf (print_no_line_fmt);
  476.       else
  477.     print_lineno ();
  478.       break;
  479.     }
  480.   fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
  481.   putchar ('\n');
  482. }
  483.  
  484. /* Print and increment the line number. */
  485.  
  486. static void
  487. print_lineno ()
  488. {
  489.   printf (print_fmt, line_no);
  490.   line_no += page_incr;
  491. }
  492.  
  493. /* Build the printf format string, based on `lineno_format'. */
  494.  
  495. static void
  496. build_print_fmt ()
  497. {
  498.   /* 12 = 10 chars for lineno_width, 1 for %, 1 for \0.  */
  499.   print_fmt = xmalloc (strlen (separator_str) + 12);
  500.   switch (lineno_format)
  501.     {
  502.     case FORMAT_RIGHT_NOLZ:
  503.       sprintf (print_fmt, "%%%dd%s", lineno_width, separator_str);
  504.       break;
  505.     case FORMAT_RIGHT_LZ:
  506.       sprintf (print_fmt, "%%0%dd%s", lineno_width, separator_str);
  507.       break;
  508.     case FORMAT_LEFT:
  509.       sprintf (print_fmt, "%%-%dd%s", lineno_width, separator_str);
  510.       break;
  511.     }
  512. }
  513.  
  514. /* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
  515.    according to `optarg'.  */
  516.  
  517. static int
  518. build_type_arg (typep, regexp)
  519.      char **typep;
  520.      struct re_pattern_buffer *regexp;
  521. {
  522.   const char *errmsg;
  523.   int rval = TRUE;
  524.   int optlen;
  525.  
  526.   switch (*optarg)
  527.     {
  528.     case 'a':
  529.     case 't':
  530.     case 'n':
  531.       *typep = optarg;
  532.       break;
  533.     case 'p':
  534.       *typep = optarg++;
  535.       optlen = strlen (optarg);
  536.       regexp->allocated = optlen * 2;
  537.       regexp->buffer = (unsigned char *) xmalloc (regexp->allocated);
  538.       regexp->translate = NULL;
  539.       regexp->fastmap = xmalloc (256);
  540.       regexp->fastmap_accurate = 0;
  541.       errmsg = re_compile_pattern (optarg, optlen, regexp);
  542.       if (errmsg)
  543.     error (1, 0, "%s", errmsg);
  544.       break;
  545.     default:
  546.       rval = FALSE;
  547.       break;
  548.     }
  549.   return rval;
  550. }
  551.  
  552. /* Print a usage message and quit. */
  553.  
  554. static void
  555. usage ()
  556. {
  557.   fprintf (stderr, "\
  558. Usage: %s [-h header-style] [-b body-style] [-f footer-style] [-p] [-d cc]\n\
  559.        [-v start-number] [-i increment] [-l lines] [-s line-separator]\n\
  560.        [-w line-no-width] [-n {ln,rn,rz}] [--header-numbering=style]\n\
  561.        [--body-numbering=style] [--footer-numbering=style]\n\
  562.        [--first-page=number] [--page-increment=number] [--no-renumber]\n\
  563.        [--join-blank-lines=number] [--number-separator=string]\n\
  564.        [--number-width=number] [--number-format={ln,rn,rz}]\n\
  565.        [--section-delimiter=cc] [--help] [--version] [file...]\n",
  566.        program_name);
  567.   exit (2);
  568. }
  569.